#include "profile/profile2.h"

using std::string;

#include <iostream>
#include <fstream>
#include <sstream>
#include <mswin/file_dir.h>
/*
template <> 
std::string profile2::get<std::string>(string name) const
{
	trim(name);
	map_type::const_iterator i = _items.find(name);
	if (i == _items.end())
		throw not_found(name);
	i->second.used = true;
	return i->second.str;
}
*/
void profile2::trim(string & s) const {
	if (s.empty())
		return;
	string blanks(" \t\n\r");
	string::size_type first = s.find_first_not_of(blanks);
	if (first == string::npos) {
		s.erase(); //string entirely consists of blanks
		return;
	}
	s.erase(0, first);
	string::size_type last = s.find_last_not_of(blanks);
	if (last != string::npos)
		s.erase(last + 1);
}

void profile2::append(std::istream & in, std::set<string> & includes, const string & src, bool nothrow)
{
	if (in.bad() || in.fail()) {
		if (nothrow)
			return;
		throw bad_source(src);
	}
	while (!in.eof()) {
		string line;
		std::getline(in, line);
		string::size_type pos;		
		// Remove the comment at the line end, if any
		if ((pos = line.find("//")) != string::npos) 
			line.erase(pos);
		if (line.empty())
			continue;		
		// Split line in statements divided by semicolon and parse them
		string::size_type first, last = static_cast<string::size_type>(-1);
		do {
			first = last + 1;
			last= line.find(';', first);
			parse(line.substr(first, last - first), includes, nothrow);
		} while (last != string::npos);
	}
}

void profile2::parse(const string & s, std::set<string> & includes, bool nothrow)
{
	if (s.compare(0, 8, "#include") == 0) {
		string file = s.substr(8);
		trim(file);
		if ((file[0] == '<' && file[file.size() - 1] == '>')
			|| (file[0] == '\"' && file[file.size() - 1] == '\"'))
			file = file.substr(1, file.size() - 2);
		else {
			if (nothrow)
				return;
			throw bad_include(s);
		}
		file = mswin::fullpath(file);
		if (includes.find(file) != includes.end())
			// already included
			return;
		std::ifstream f(file.c_str());
		if (!f.fail() && !f.bad())
			includes.insert(file);
		mswin::cwd cwd(mswin::parent(file));
		append(f, includes, string("file <") + file + ">", nothrow);
		return;
	}
	string::size_type pos = s.find('=');
	string name = s.substr(0, pos), 
		value = (pos != string::npos) ? s.substr(pos + 1) : "";
	trim(name);
	if (name.empty())
		return;
	trim(value);
	_items[name].str = value;
}

profile2::profile2()
{
	_output = output_all;
}

profile2::profile2(std::istream & in, bool nothrow)
{
	_output = output_all;
	append(in, nothrow);
}

profile2::profile2(const string & fname, bool nothrow)
{
	_output = output_all;
	appendf(fname, nothrow);
}

profile2::~profile2()
{
}

void profile2::append(std::istream & in, bool nothrow)
{
	std::set<string> includes;
	append(in, includes, "", nothrow);
}

void profile2::appendf(const string & fname, bool nothrow)
{
	std::set<string> includes;
	includes.insert(mswin::fullpath(fname));
	mswin::cwd cwd(mswin::parent(fname));

	std::ifstream fin(fname.c_str());
	append(fin, includes, string("file <") + fname + ">", nothrow);
}

void profile2::appends(const string & s, bool nothrow)
{
	std::set<string> includes;
	
	std::istringstream sin(s);
	append(sin, includes, string("string <") + s + ">", nothrow);
}

void profile2::append(const profile2 & p)
{
	map_type::const_iterator i;
	for (i = p._items.begin(); i != p._items.end(); ++i)
		_items[i->first].str = i->second.str;
}

void profile2::clear()
{
	_items.clear();
}

void profile2::swap(profile2 & partner)
{
	_items.swap(partner._items);
}

std::ostream & operator << (std::ostream & out, const profile2 & p)
{
	profile2::map_type::const_iterator i;
	for (i = p._items.begin(); i != p._items.end(); ++i) {
		if (i->second.used && p._output == profile2::output_unused)
			continue;
		if (!i->second.used && p._output == profile2::output_used)
			continue;
		out << i->first;
		if (!i->second.str.empty())
			out << " = " << i->second.str;
		out << "\n";
	}
	return out;
}

bool profile2::unused(string * pname) const
{
	map_type::const_iterator i;
	for (i = _items.begin(); i != _items.end(); ++i) {
		if (!i->second.used) {
			if (pname)
				* pname = i->first;
			return true;
		}
	}
	return false;
}

bool profile2::query(string name) const
{
	trim(name);
	map_type::const_iterator i = _items.find(name);
	if (i == _items.end())
		return false;
	i->second.used = true;
	return true;
}


